/*-------------------------------------------------------------------------
 *
 *	  EUC_JIS_2004, SHIFT_JIS_2004
 *
 * Copyright (c) 2007-2022, PostgreSQL Global Development Group
 *
 * IDENTIFICATION
 *	  src/backend/utils/mb/conversion_procs/euc2004_sjis2004/euc2004_sjis2004.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"
#include "fmgr.h"
#include "mb/pg_wchar.h"

PG_MODULE_MAGIC;

PG_FUNCTION_INFO_V1(euc_jis_2004_to_shift_jis_2004);
PG_FUNCTION_INFO_V1(shift_jis_2004_to_euc_jis_2004);

static int	fc_euc_jis_20042shift_jis_2004(const unsigned char *fc_euc, unsigned char *fc_p, int fc_len, bool fc_noError);
static int	fc_shift_jis_20042euc_jis_2004(const unsigned char *fc_sjis, unsigned char *fc_p, int fc_len, bool fc_noError);

/* ----------
 * conv_proc(
 *		INTEGER,	-- 源编码id
 *		INTEGER,	-- 目标编码id
 *		CSTRING,	-- 源字符串（以null结尾的C字符串）
 *		CSTRING,	-- 目标字符串（以null结尾的C字符串）
 *		INTEGER,	-- 源字符串长度
 *		BOOL		-- 如果为真，转换失败时不抛出错误
 * ) 返回INTEGER;
 *
 * 返回成功转换的字节数。
 * ----------
 */

Datum euc_jis_2004_to_shift_jis_2004(PG_FUNCTION_ARGS)
{
	unsigned char *fc_src = (unsigned char *) PG_GETARG_CSTRING(2);
	unsigned char *fc_dest = (unsigned char *) PG_GETARG_CSTRING(3);
	int			fc_len = PG_GETARG_INT32(4);
	bool		fc_noError = PG_GETARG_BOOL(5);
	int			fc_converted;

	CHECK_ENCODING_CONVERSION_ARGS(PG_EUC_JIS_2004, PG_SHIFT_JIS_2004);

	fc_converted = fc_euc_jis_20042shift_jis_2004(fc_src, fc_dest, fc_len, fc_noError);

	PG_RETURN_INT32(fc_converted);
}

Datum shift_jis_2004_to_euc_jis_2004(PG_FUNCTION_ARGS)
{
	unsigned char *fc_src = (unsigned char *) PG_GETARG_CSTRING(2);
	unsigned char *fc_dest = (unsigned char *) PG_GETARG_CSTRING(3);
	int			fc_len = PG_GETARG_INT32(4);
	bool		fc_noError = PG_GETARG_BOOL(5);
	int			fc_converted;

	CHECK_ENCODING_CONVERSION_ARGS(PG_SHIFT_JIS_2004, PG_EUC_JIS_2004);

	fc_converted = fc_shift_jis_20042euc_jis_2004(fc_src, fc_dest, fc_len, fc_noError);

	PG_RETURN_INT32(fc_converted);
}

/*
 * EUC_JIS_2004 -> SHIFT_JIS_2004
 */
static int fc_euc_jis_20042shift_jis_2004(const unsigned char *fc_euc, unsigned char *fc_p, int fc_len, bool fc_noError)
{
	const unsigned char *fc_start = fc_euc;
	int			fc_c1,
				fc_ku,
				fc_ten;
	int			fc_l;

	while (fc_len > 0)
	{
		fc_c1 = *fc_euc;
		if (!IS_HIGHBIT_SET(fc_c1))
		{
			/* ASCII */
			if (fc_c1 == 0)
			{
				if (fc_noError)
					break;
				report_invalid_encoding(PG_EUC_JIS_2004,
										(const char *) fc_euc, fc_len);
			}
			*fc_p++ = fc_c1;
			fc_euc++;
			fc_len--;
			continue;
		}

		fc_l = pg_encoding_verifymbchar(PG_EUC_JIS_2004, (const char *) fc_euc, fc_len);

		if (fc_l < 0)
		{
			if (fc_noError)
				break;
			report_invalid_encoding(PG_EUC_JIS_2004,
									(const char *) fc_euc, fc_len);
		}

		if (fc_c1 == SS2 && fc_l == 2)	/* JIS X 0201 kana？ */
		{
			*fc_p++ = fc_euc[1];
		}
		else if (fc_c1 == SS3 && fc_l == 3)	/* JIS X 0213 平面 2? */
		{
			fc_ku = fc_euc[1] - 0xa0;
			fc_ten = fc_euc[2] - 0xa0;

			switch (fc_ku)
			{
				case 1:
				case 3:
				case 4:
				case 5:
				case 8:
				case 12:
				case 13:
				case 14:
				case 15:
					*fc_p++ = ((fc_ku + 0x1df) >> 1) - (fc_ku >> 3) * 3;
					break;
				default:
					if (fc_ku >= 78 && fc_ku <= 94)
					{
						*fc_p++ = (fc_ku + 0x19b) >> 1;
					}
					else
					{
						if (fc_noError)
							break;
						report_invalid_encoding(PG_EUC_JIS_2004,
												(const char *) fc_euc, fc_len);
					}
			}

			if (fc_ku % 2)
			{
				if (fc_ten >= 1 && fc_ten <= 63)
					*fc_p++ = fc_ten + 0x3f;
				else if (fc_ten >= 64 && fc_ten <= 94)
					*fc_p++ = fc_ten + 0x40;
				else
				{
					if (fc_noError)
						break;
					report_invalid_encoding(PG_EUC_JIS_2004,
											(const char *) fc_euc, fc_len);
				}
			}
			else
				*fc_p++ = fc_ten + 0x9e;
		}

		else if (fc_l == 2)		/* JIS X 0213 平面 1? */
		{
			fc_ku = fc_c1 - 0xa0;
			fc_ten = fc_euc[1] - 0xa0;

			if (fc_ku >= 1 && fc_ku <= 62)
				*fc_p++ = (fc_ku + 0x101) >> 1;
			else if (fc_ku >= 63 && fc_ku <= 94)
				*fc_p++ = (fc_ku + 0x181) >> 1;
			else
			{
				if (fc_noError)
					break;
				report_invalid_encoding(PG_EUC_JIS_2004,
										(const char *) fc_euc, fc_len);
			}

			if (fc_ku % 2)
			{
				if (fc_ten >= 1 && fc_ten <= 63)
					*fc_p++ = fc_ten + 0x3f;
				else if (fc_ten >= 64 && fc_ten <= 94)
					*fc_p++ = fc_ten + 0x40;
				else
				{
					if (fc_noError)
						break;
					report_invalid_encoding(PG_EUC_JIS_2004,
											(const char *) fc_euc, fc_len);
				}
			}
			else
				*fc_p++ = fc_ten + 0x9e;
		}
		else
		{
			if (fc_noError)
				break;
			report_invalid_encoding(PG_EUC_JIS_2004,
									(const char *) fc_euc, fc_len);
		}

		fc_euc += fc_l;
		fc_len -= fc_l;
	}
	*fc_p = '\0';

	return fc_euc - fc_start;
}

/* 
 * 返回由第二个字节指示的 SHIFT_JIS_2004 "ku" 代码
 * *ku = 0: "ku" = 偶数
 * *ku = 1: "ku" = 奇数
 */
static int get_ten(int fc_b, int *fc_ku)
{
	int			fc_ten;

	if (fc_b >= 0x40 && fc_b <= 0x7e)
	{
		fc_ten = fc_b - 0x3f;
		*fc_ku = 1;
	}
	else if (fc_b >= 0x80 && fc_b <= 0x9e)
	{
		fc_ten = fc_b - 0x40;
		*fc_ku = 1;
	}
	else if (fc_b >= 0x9f && fc_b <= 0xfc)
	{
		fc_ten = fc_b - 0x9e;
		*fc_ku = 0;
	}
	else
	{
		fc_ten = -1;				/* 错误 */
		*fc_ku = 0;				/* 保持编译器安静 */
	}
	return fc_ten;
}

/* 
 * SHIFT_JIS_2004 ---> EUC_JIS_2004
 */

static int fc_shift_jis_20042euc_jis_2004(const unsigned char *fc_sjis, unsigned char *fc_p, int fc_len, bool fc_noError)
{
	const unsigned char *fc_start = fc_sjis;
	int			fc_c1;
	int			fc_ku,
				fc_ten,
				fc_kubun;
	int			fc_plane;
	int			fc_l;

	while (fc_len > 0)
	{
		fc_c1 = *fc_sjis;

		if (!IS_HIGHBIT_SET(fc_c1))
		{
			/* ASCII */
			if (fc_c1 == 0)
			{
				if (fc_noError)
					break;
				report_invalid_encoding(PG_SHIFT_JIS_2004,
										(const char *) fc_sjis, fc_len);
			}
			*fc_p++ = fc_c1;
			fc_sjis++;
			fc_len--;
			continue;
		}

		fc_l = pg_encoding_verifymbchar(PG_SHIFT_JIS_2004, (const char *) fc_sjis, fc_len);

		if (fc_l < 0 || fc_l > fc_len)
		{
			if (fc_noError)
				break;
			report_invalid_encoding(PG_SHIFT_JIS_2004,
									(const char *) fc_sjis, fc_len);
		}

		if (fc_c1 >= 0xa1 && fc_c1 <= 0xdf && fc_l == 1)
		{
			/* JIS X0201 (1 字节平假名) */
			*fc_p++ = SS2;
			*fc_p++ = fc_c1;
		}
		else if (fc_l == 2)
		{
			int			fc_c2 = fc_sjis[1];

			fc_plane = 1;
			fc_ku = 1;
			fc_ten = 1;

			/* 
			 * JIS X 0213
 */
			if (fc_c1 >= 0x81 && fc_c1 <= 0x9f)	/* 平面 1 1ku-62ku */
			{
				fc_ku = (fc_c1 << 1) - 0x100;
				fc_ten = get_ten(fc_c2, &fc_kubun);
				if (fc_ten < 0)
				{
					if (fc_noError)
						break;
					report_invalid_encoding(PG_SHIFT_JIS_2004,
											(const char *) fc_sjis, fc_len);
				}
				fc_ku -= fc_kubun;
			}
			else if (fc_c1 >= 0xe0 && fc_c1 <= 0xef)	/* 平面 1 62ku-94ku */
			{
				fc_ku = (fc_c1 << 1) - 0x180;
				fc_ten = get_ten(fc_c2, &fc_kubun);
				if (fc_ten < 0)
				{
					if (fc_noError)
						break;
					report_invalid_encoding(PG_SHIFT_JIS_2004,
											(const char *) fc_sjis, fc_len);
				}
				fc_ku -= fc_kubun;
			}
			else if (fc_c1 >= 0xf0 && fc_c1 <= 0xf3)	/* 平面 2
												 * 1,3,4,5,8,12,13,14,15 ku */
			{
				fc_plane = 2;
				fc_ten = get_ten(fc_c2, &fc_kubun);
				if (fc_ten < 0)
				{
					if (fc_noError)
						break;
					report_invalid_encoding(PG_SHIFT_JIS_2004,
											(const char *) fc_sjis, fc_len);
				}
				switch (fc_c1)
				{
					case 0xf0:
						fc_ku = fc_kubun == 0 ? 8 : 1;
						break;
					case 0xf1:
						fc_ku = fc_kubun == 0 ? 4 : 3;
						break;
					case 0xf2:
						fc_ku = fc_kubun == 0 ? 12 : 5;
						break;
					default:
						fc_ku = fc_kubun == 0 ? 14 : 13;
						break;
				}
			}
			else if (fc_c1 >= 0xf4 && fc_c1 <= 0xfc)	/* 平面 2 78-94ku */
			{
				fc_plane = 2;
				fc_ten = get_ten(fc_c2, &fc_kubun);
				if (fc_ten < 0)
				{
					if (fc_noError)
						break;
					report_invalid_encoding(PG_SHIFT_JIS_2004,
											(const char *) fc_sjis, fc_len);
				}
				if (fc_c1 == 0xf4 && fc_kubun == 1)
					fc_ku = 15;
				else
					fc_ku = (fc_c1 << 1) - 0x19a - fc_kubun;
			}
			else
			{
				if (fc_noError)
					break;
				report_invalid_encoding(PG_SHIFT_JIS_2004,
										(const char *) fc_sjis, fc_len);
			}

			if (fc_plane == 2)
				*fc_p++ = SS3;

			*fc_p++ = fc_ku + 0xa0;
			*fc_p++ = fc_ten + 0xa0;
		}
		fc_sjis += fc_l;
		fc_len -= fc_l;
	}
	*fc_p = '\0';

	return fc_sjis - fc_start;
}
